package com.alu.csv;

import static org.junit.Assert.*;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.Test;

public class SortCSV {

	// 定义一个分隔符
	private static String SEP = ",";
	
	// 用于存储CSV文件表格头信息
	private String header;
	
	/**
	 * 读取文件
	 * 
	 * @param fileName
	 * @return List<List<String>>
	 */
	private List<List<String>> read(String fileName) {
		List<List<String>> result = new ArrayList<>();
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(fileName));
			
			boolean isHeader = true;
			readCSV: for(;;) {
				String line = br.readLine();
				
				if(null == line) {
					break readCSV;
				}
				
				// 第一行是头信息，不要用来排序，但是需要保存下来以便回写
				if(isHeader) {
					isHeader = false;
					this.header = line;
					continue;
				}
				
				// 用逗号切开并保存下来
				result.add(Arrays.asList(line.split(SEP)));
			}
			
		} catch(IOException e) {
			throw new RuntimeException("读取CSV文件出错了："+e);
		} finally {
			if(null != br) {
				try {
					br.close();
				} catch (IOException e) {
				} finally {
					br = null;
				}
			}
		}
		
		return result;
	}
	
	/**
	 * 实现一个比较器逻辑
	 * 
	 * @param cols 传入的需要比较的列号，从0开始，0代表第一列
	 * @return Comparator<List<String>>
	 */
	private Comparator<List<String>> getComparator(int...cols) {
		return new Comparator<List<String>>() {
			@Override
			public int compare(List<String> l1, List<String> l2) {
				// 默认相等
				int result = 0;
				// 根据列标分别取两行数据进行比较，如果有大小则返回，没有大小则对下一个列标进行比较
				for(int col : cols) {
					String v1 = l1.get(col);
					String v2 = l2.get(col);
					int c = v1.compareTo(v2);
					if(0 != c) {
						return c;
					}
				}
				return result;
			}
		};
	}
	
	/**
	 * 写CSV的代码
	 * 
	 * @param sortedContent 已经排序好的内容
	 * @param fileName
	 */
	private void write(List<List<String>> sortedContent, String fileName) {
		BufferedWriter bw = null;
		try {
			bw = new BufferedWriter(new FileWriter(fileName));
			
			// 先把表头信息写进去
			bw.write(this.header+System.lineSeparator());
			
			// 循环写值
			for(List<String> line : sortedContent) {
				StringBuffer sb = new StringBuffer();
				int i=0;
				// 将一行的值拼进去
				for(; i<line.size()-1; i++) {
					sb.append(line.get(i)+SEP);
				}
				// 最后一个值单独拼，避免最后有逗号
				sb.append(line.get(i)+System.lineSeparator());
				bw.write(sb.toString());
			}
		} catch(IOException e) {
			throw new RuntimeException("写入CSV文件失败："+e);
		} finally {
			try {
				bw.close();
			} catch (IOException e) {
			} finally {
				bw = null;
			}
		}
	}
	
	/**
	 * 本类中唯一对外公开的方法，用于执行整个读入，排序，写出操作
	 * 
	 * @param inputFileName 输入文件名
	 * @param outputFileName 输出文件名
	 * @param cols 传入的需要比较的列号，从0开始，0代表第一列
	 */
	public void sort(String inputFileName, String outputFileName, int...cols) {
		List<List<String>> input = this.read(inputFileName);
		Collections.sort(input, this.getComparator(cols));
		this.write(input, outputFileName);
	}
	
	////////////////////////////////////////////////////////
	// 单元测试，因私有方法故写在类内部
	////////////////////////////////////////////////////////
	@Test
	public void testRead() {
		SortCSV sortCSV = new SortCSV();
		List<List<String>> list = sortCSV.read("input.csv");
		assertFalse(list.isEmpty());
		assertEquals("Tom", list.get(0).get(0));
	}
	
	@Test
	public void testGetComparator() {
		SortCSV sortCSV = new SortCSV();
		Comparator<List<String>> comparator = sortCSV.getComparator(0,1,2);
		assertNotNull(comparator);
		List<String> l1 = new ArrayList<>();
		l1.add("2");
		List<String> l2 = new ArrayList<>();
		l2.add("3");
		assertEquals(-1, comparator.compare(l1, l2));
	}
	
	@Test
	public void testWrite() {
		SortCSV sortCSV = new SortCSV();
		List<List<String>> list = sortCSV.read("input.csv");
		sortCSV.write(list, "testWrite.csv");
		list = sortCSV.read("testWrite.csv");
		assertFalse(list.isEmpty());
		assertEquals("Tom", list.get(0).get(0));
		assertEquals("Joan", list.get(3).get(0));
	}
	
	@Test
	public void testSort() {
		SortCSV sortCSV = new SortCSV();
		
		// 按姓名排序，第一个应该是Arun
		sortCSV.sort("input.csv", "sortBy1.csv", 0);
		List<List<String>> sortBy1 = sortCSV.read("sortBy1.csv");
		assertFalse(sortBy1.isEmpty());
		assertEquals("Arun", sortBy1.get(0).get(0));
		
		// 先按性别排，再按级别排，第一个应该是Rose
		sortCSV.sort("input.csv", "sortBy23.csv", 1, 2);
		List<List<String>> sortBy23 = sortCSV.read("sortBy23.csv");
		assertFalse(sortBy23.isEmpty());
		assertEquals("Ross", sortBy23.get(0).get(0));
	}
}
